/*
 * Copyright 2016 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.blockly.ohos.ui.fieldview;

import com.google.blockly.ohos.ResourceTable;
import com.google.blockly.ohos.ui.WorkspaceHelper;
import com.google.blockly.model.Field;
import com.google.blockly.model.FieldNumber;
import com.google.blockly.utils.ImageUtils;
import com.google.blockly.utils.LogUtils;
import ohos.agp.components.*;
import ohos.agp.components.element.Element;
import ohos.agp.render.Canvas;
import ohos.agp.utils.Rect;
import ohos.agp.utils.TextTool;
import ohos.app.Context;
import ohos.global.configuration.Configuration;

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;

import static ohos.agp.components.InputAttribute.PATTERN_NUMBER;

/**
 * A basic UI for {@link FieldNumber}.
 */
public class BasicFieldNumberView extends TextField implements FieldView, Component.FocusChangedListener, Text.EditorActionListener {
    private static final String TAG = "BasicFieldNumberView";

    protected DecimalFormatSymbols mLocalizedDecimalSymbols;
    protected String mLocalizedGroupingSeparator;
    protected NumberFormat mLocalizedNumberParser;

    protected boolean mAllowExponent = true;
    protected boolean mTextIsValid = false;

    private boolean mIsUpdatingField = false;

    private double mLatestMin = FieldNumber.NO_CONSTRAINT;
    private double mLatestMax = FieldNumber.NO_CONSTRAINT;
    private double mLatestPrecision = FieldNumber.NO_CONSTRAINT;
    private NumberFormat mLocalizedNumberFormat;

    private final TextObserver mWatcher = new TextObserver() {
        @Override
        public void onTextUpdated(String text, int i, int i1, int i2) {
            if (mNumberField != null) {
                try {
                    mIsUpdatingField = true;
                    if (text.length() > 0) {
                        // Attempt to parse numbers using context's locale,
                        // ignoring the locale's grouping marker (b/c is causes parse errors).
                        String textWithoutGrouping =
                                text.toString().replace(mLocalizedGroupingSeparator, "");
                        try {
                            double newValue =
                                    mLocalizedNumberParser.parse(textWithoutGrouping).doubleValue();
                            mNumberField.setValue(newValue);
                            setTextValid(true);
                        } catch (ParseException e) {
                            // Failed to parse intermediate
                            setTextValid(false);
                        }
                    } else {
                        // Empty string always overwrites value as if it was 0.
                        mNumberField.setValue(0);
                        setTextValid(false);
                    }
                } finally {
                    mIsUpdatingField = false;
                }
                invalidate();
            }
        }
    };

    private final Field.Observer mFieldObserver = new Field.Observer() {
        @Override
        public void onValueChanged(Field field, String oldStrValue, String newStrValue) {
            if (mIsUpdatingField) {
                return;
            }
            updateLocalizedNumberFormatIfConstraintsChanged();
            if (field != mNumberField) {  // Potential race condition if view's field changes.
                LogUtils.w(TAG, "Received value change from unexpected field.");
                return;
            }
            try {
                String text = getText();
                Double value = mNumberField.getValue();

                // Because numbers can have different string representations,
                // only overwrite if the parsed value differs.
                if (TextTool.isNullOrEmpty(text) || mLocalizedNumberParser.parse(text).doubleValue() != value) {
                    setText(mLocalizedNumberFormat.format(mNumberField.getValue()));
                }
            } catch (ParseException e) {
                setText(mLocalizedNumberFormat.format(mNumberField.getValue()));
            }
        }
    };

    private final DrawTask mDrawTask = new DrawTask() {
        @Override
        public void onDraw(Component component, Canvas canvas) {
            Element bg = ImageUtils.getPatchDrawable(getContext(), ResourceTable.Media_edit_text_background_default);
            if (bg != null) {
                bg.setBounds(new Rect(0, 0, component.getEstimatedWidth(), component.getEstimatedHeight()));
                bg.drawToCanvas(canvas);
            }
        }
    };

    protected FieldNumber mNumberField;

    public BasicFieldNumberView(Context context) {
        super(context);
        setFocusChangedListener(this);
        onFinishInflate();
        addDrawTask(mDrawTask);
    }

    public BasicFieldNumberView(Context context, AttrSet attrs) {
        super(context, attrs);
        setFocusChangedListener(this);
        onFinishInflate();
        addDrawTask(mDrawTask);
    }


    protected void onFinishInflate() {
        // Get a localized, but otherwise permissive
        mLocalizedDecimalSymbols = new DecimalFormatSymbols(getPrimaryLocale());
        mLocalizedGroupingSeparator =
                Character.toString(mLocalizedDecimalSymbols.getGroupingSeparator());
        mLocalizedNumberParser =
                new DecimalFormat("#.#", mLocalizedDecimalSymbols);

        addTextObserver(mWatcher);
        updateInputMethod();
    }


    @Override
    public void onFocusChange(Component component, boolean focused) {
        if (!focused && mNumberField != null) {
            CharSequence text = getText();
            if (text.length() == 0) {
                // Replace empty string with value closest to zero.
                mNumberField.setValue(0);
            }
            updateLocalizedNumberFormatIfConstraintsChanged();
            setText(mLocalizedNumberFormat.format(mNumberField.getValue()));
            setTextValid(true);
        }
    }

    @Override
    public void setField(Field field) {
        FieldNumber number = (FieldNumber) field;
        if (mNumberField == number) {
            return;
        }

        if (mNumberField != null) {
            mNumberField.unregisterObserver(mFieldObserver);
        }
        mNumberField = number;
        if (mNumberField != null) {
            updateInputMethod();
            updateLocalizedNumberFormat();
            setText(mLocalizedNumberFormat.format(mNumberField.getValue()));
            setTextInputType(PATTERN_NUMBER);
            mNumberField.registerObserver(mFieldObserver);
        } else {
            setText("");
        }
    }

    @Override
    public Field getField() {
        return mNumberField;
    }

    public boolean isTextValid() {
        return mTextIsValid;
    }

    protected void setTextValid(boolean textIsValid) {
        mTextIsValid = textIsValid;
    }

    /**
     * Override onDragEvent to stop blocks from being dropped into text fields.  If the dragged
     * information is anything but a block, let the standard EditText drag interface take care of
     * it.
     *
     * @param event The {@link DragEvent} to respond to.
     * @return False if the dragged data is a block, whatever a normal EditText would return
     * otherwise.
     */
    @Override
    public boolean onDrag(Component component, DragEvent event) {
        if (WorkspaceHelper.isBlockDrag(getContext(), event)) {
            return false;
        }
        return super.onDrag(component, event);
    }

    @Override
    public void unlinkField() {
        setField(null);
    }

    protected void updateInputMethod() {
        boolean hasNumberField = mNumberField != null;
        setEnabled(hasNumberField);

        if (hasNumberField) {
            int imeOptions = InputAttribute.PATTERN_NUMBER;
            StringBuilder allowedChars = new StringBuilder("0123456789");
            if (mAllowExponent) {
                allowedChars.append("e");
            }

            if (!mNumberField.hasMinimum() || mNumberField.getMinimumValue() < 0) {
                allowedChars.append("-");
            }
            if (!mNumberField.isInteger()) {
                allowedChars.append(mLocalizedDecimalSymbols.getDecimalSeparator());
            }
            allowedChars.append(mLocalizedDecimalSymbols.getGroupingSeparator());

            setInputMethodOption(imeOptions);
            setEditorActionListener(this);
        }
    }

    protected void updateLocalizedNumberFormat() {
        mLatestMin = mNumberField.getMinimumValue();
        mLatestMax = mNumberField.getMaximumValue();
        mLatestPrecision = mNumberField.getPrecision();
        mLocalizedNumberFormat = mNumberField.getNumberFormatForLocale(getPrimaryLocale());
    }

    protected void updateLocalizedNumberFormatIfConstraintsChanged() {
        if (mNumberField.getMinimumValue() != mLatestMin
                || mNumberField.getMaximumValue() != mLatestMax
                || mNumberField.getPrecision() != mLatestPrecision) {
            updateLocalizedNumberFormat();
        }
    }

    /**
     * @return The primary locale for the view's context.
     */
    public Locale getPrimaryLocale() {
        Configuration configuration = getContext().getResourceManager().getConfiguration();
        return configuration.getLocaleProfile().getLocales()[0];
    }

    @Override
    public boolean onTextEditorAction(int action) {
        //2 切换 3 搜索 4 发送
        return false;
    }
}
